Add new GtkActionable interface
authorRyan Lortie <desrt@desrt.ca>
Fri, 6 Jan 2012 03:22:06 +0000 (22:22 -0500)
committerRyan Lortie <desrt@desrt.ca>
Mon, 9 Jan 2012 14:02:06 +0000 (15:02 +0100)
This is the interface for GtkWidgets that can be associated with an
action on a GtkAppicationWindow or associated GtkApplication.

It essentially features 'action-name' and 'action-target' properties
with some associated convenience API.

This interface is implemented by GtkButton and GtkToolButton.

https://bugzilla.gnome.org/show_bug.cgi?id=667394

gtk/Makefile.am
gtk/gsimpleactionobserver.c [new file with mode: 0644]
gtk/gsimpleactionobserver.h [new file with mode: 0644]
gtk/gtk.h
gtk/gtkactionable.c [new file with mode: 0644]
gtk/gtkactionable.h [new file with mode: 0644]
gtk/gtkapplicationprivate.h
gtk/gtkapplicationwindow.c
gtk/gtkbutton.c
gtk/gtkbuttonprivate.h
gtk/gtktoolbutton.c

index ab46644c240e1a849e3d509be30bfa0cb9c96805..97719ace6ff5b87595e7515a056085f3bbf9af14 100644 (file)
@@ -172,6 +172,7 @@ gtk_public_h_sources =              \
        gtkaccellabel.h         \
        gtkaccelmap.h           \
        gtkaccessible.h         \
+       gtkactionable.h         \
        gtkaction.h             \
        gtkactiongroup.h        \
        gtkactivatable.h        \
@@ -392,6 +393,7 @@ gtk_private_type_h_sources = \
 # GTK+ header files that don't get installed
 gtk_private_h_sources =                \
        gactionmuxer.h          \
+       gsimpleactionobserver.h \
        gactionobserver.h       \
        gactionobservable.h     \
        gtkapplicationprivate.h \
@@ -512,8 +514,10 @@ deprecated_c_sources =                     \
 gtk_base_c_sources =           \
        $(deprecated_c_sources) \
        gactionmuxer.c          \
+       gsimpleactionobserver.c \
        gactionobserver.c       \
        gactionobservable.c     \
+       gtkactionable.c         \
        gtkquery.c              \
        gtksearchengine.c       \
        gtksearchenginesimple.c \
diff --git a/gtk/gsimpleactionobserver.c b/gtk/gsimpleactionobserver.c
new file mode 100644 (file)
index 0000000..fdbe4bf
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright © 2012 Canonical Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * Authors: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include "config.h"
+
+#include "gsimpleactionobserver.h"
+#include "gactionobservable.h"
+
+typedef GObjectClass GSimpleActionObserverClass;
+struct _GSimpleActionObserver
+{
+  GObject parent_instance;
+
+  GActionGroup *action_group;
+  gchar *action_name;
+  GVariant *target;
+
+  gboolean can_activate;
+  gboolean active;
+  gboolean enabled;
+
+  gint reporting;
+};
+
+static void g_simple_action_observer_init_iface (GActionObserverInterface *iface);
+G_DEFINE_TYPE_WITH_CODE (GSimpleActionObserver, g_simple_action_observer, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVER, g_simple_action_observer_init_iface));
+
+enum
+{
+  PROP_0,
+  PROP_ACTIVE,
+  PROP_ENABLED,
+  N_PROPS
+};
+
+static GParamSpec *g_simple_action_observer_pspecs[N_PROPS];
+
+static void
+g_simple_action_observer_action_added (GActionObserver    *g_observer,
+                                       GActionObservable  *observable,
+                                       const gchar        *action_name,
+                                       const GVariantType *parameter_type,
+                                       gboolean            enabled,
+                                       GVariant           *state)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
+  gboolean active;
+
+  /* we can only activate if we have the correct type of parameter */
+  observer->can_activate = (observer->target == NULL && parameter_type == NULL) ||
+                            (observer->target != NULL && parameter_type != NULL &&
+                             g_variant_is_of_type (observer->target, parameter_type));
+
+  if (observer->can_activate)
+    {
+      if (observer->target != NULL && state != NULL)
+        active = g_variant_equal (state, observer->target);
+
+      else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+        active = g_variant_get_boolean (state);
+
+      else
+        active = FALSE;
+
+      if (active != observer->active)
+        {
+          observer->active = active;
+          observer->reporting++;
+          g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ACTIVE]);
+          observer->reporting--;
+        }
+
+      if (enabled != observer->enabled)
+        {
+          observer->enabled = enabled;
+          g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ENABLED]);
+        }
+    }
+}
+
+static void
+g_simple_action_observer_action_enabled_changed (GActionObserver   *g_observer,
+                                                 GActionObservable *observable,
+                                                 const gchar       *action_name,
+                                                 gboolean           enabled)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
+
+  if (!observer->can_activate)
+    return;
+
+  if (enabled != observer->enabled)
+    {
+      observer->enabled = enabled;
+      g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ENABLED]);
+    }
+}
+
+static void
+g_simple_action_observer_action_state_changed (GActionObserver   *g_observer,
+                                               GActionObservable *observable,
+                                               const gchar       *action_name,
+                                               GVariant          *state)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
+  gboolean active = FALSE;
+
+  if (!observer->can_activate)
+    return;
+
+  if (observer->target)
+    active = g_variant_equal (state, observer->target);
+
+  else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+    active = g_variant_get_boolean (state);
+
+  if (active != observer->active)
+    {
+      observer->active = active;
+      observer->reporting++;
+      g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ACTIVE]);
+      observer->reporting--;
+    }
+}
+
+static void
+g_simple_action_observer_action_removed (GActionObserver   *g_observer,
+                                         GActionObservable *observable,
+                                         const gchar       *action_name)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
+
+  if (!observer->can_activate)
+    return;
+
+  observer->can_activate = FALSE;
+
+  if (observer->active)
+    {
+      observer->active = FALSE;
+      observer->reporting++;
+      g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ACTIVE]);
+      observer->reporting--;
+    }
+
+  if (observer->enabled)
+    {
+      observer->enabled = FALSE;
+      g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ENABLED]);
+    }
+}
+
+static void
+g_simple_action_observer_get_property (GObject *object, guint prop_id,
+                                       GValue *value, GParamSpec *pspec)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACTIVE:
+      g_value_set_boolean (value, observer->active);
+      break;
+
+    case PROP_ENABLED:
+      g_value_set_boolean (value, observer->enabled);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+g_simple_action_observer_finalize (GObject *object)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (object);
+
+  g_object_unref (observer->action_group);
+  g_free (observer->action_name);
+
+  if (observer->target)
+    g_variant_unref (observer->target);
+
+  G_OBJECT_CLASS (g_simple_action_observer_parent_class)
+    ->finalize (object);
+}
+
+static void
+g_simple_action_observer_init (GSimpleActionObserver *observer)
+{
+}
+
+static void
+g_simple_action_observer_init_iface (GActionObserverInterface *iface)
+{
+  iface->action_added = g_simple_action_observer_action_added;
+  iface->action_enabled_changed = g_simple_action_observer_action_enabled_changed;
+  iface->action_state_changed = g_simple_action_observer_action_state_changed;
+  iface->action_removed = g_simple_action_observer_action_removed;
+}
+
+static void
+g_simple_action_observer_class_init (GObjectClass *class)
+{
+  class->get_property = g_simple_action_observer_get_property;
+  class->finalize = g_simple_action_observer_finalize;
+
+  g_simple_action_observer_pspecs[PROP_ACTIVE] = g_param_spec_boolean ("active", "active", "active", FALSE,
+                                                                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_simple_action_observer_pspecs[PROP_ENABLED] = g_param_spec_boolean ("enabled", "enabled", "enabled", FALSE,
+                                                                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_properties (class, N_PROPS, g_simple_action_observer_pspecs);
+}
+
+GSimpleActionObserver *
+g_simple_action_observer_new (GActionObservable *observable,
+                              const gchar       *action_name,
+                              GVariant          *target)
+{
+  GSimpleActionObserver *observer;
+  const GVariantType *type;
+  gboolean enabled;
+  GVariant *state;
+
+  observer = g_object_new (G_TYPE_SIMPLE_ACTION_OBSERVER, NULL);
+  observer->action_group = g_object_ref (observable);
+  observer->action_name = g_strdup (action_name);
+  if (target)
+    observer->target = g_variant_ref_sink (target);
+
+  g_action_observable_register_observer (observable, action_name, G_ACTION_OBSERVER (observer));
+
+  if (g_action_group_query_action (observer->action_group, action_name, &enabled, &type, NULL, NULL, &state))
+    {
+      g_simple_action_observer_action_added (G_ACTION_OBSERVER (observer), observable,
+                                             action_name, type, enabled, state);
+      if (state)
+        g_variant_unref (state);
+    }
+
+  return observer;
+}
+
+void
+g_simple_action_observer_activate (GSimpleActionObserver *observer)
+{
+  g_return_if_fail (G_IS_SIMPLE_ACTION_OBSERVER (observer));
+
+  if (observer->can_activate && !observer->reporting)
+    g_action_group_activate_action (G_ACTION_GROUP (observer->action_group),
+                                    observer->action_name, observer->target);
+}
+
+gboolean
+g_simple_action_observer_get_active (GSimpleActionObserver *observer)
+{
+  g_return_val_if_fail (G_IS_SIMPLE_ACTION_OBSERVER (observer), FALSE);
+
+  return observer->active;
+}
+
+gboolean
+g_simple_action_observer_get_enabled (GSimpleActionObserver *observer)
+{
+  g_return_val_if_fail (G_IS_SIMPLE_ACTION_OBSERVER (observer), FALSE);
+
+  return observer->enabled;
+}
diff --git a/gtk/gsimpleactionobserver.h b/gtk/gsimpleactionobserver.h
new file mode 100644 (file)
index 0000000..c3340f7
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2012 Canonical Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * Authors: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#ifndef __G_SIMPLE_ACTION_OBSERVER_H__
+#define __G_SIMPLE_ACTION_OBSERVER_H__
+
+#include "gactionobserver.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_SIMPLE_ACTION_OBSERVER                       (g_simple_action_observer_get_type ())
+#define G_SIMPLE_ACTION_OBSERVER(inst)                      (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_SIMPLE_ACTION_OBSERVER,                          \
+                                                             GSimpleActionObserver))
+#define G_IS_SIMPLE_ACTION_OBSERVER(inst)                   (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_SIMPLE_ACTION_OBSERVER))
+
+typedef struct _GSimpleActionObserver                       GSimpleActionObserver;
+
+G_GNUC_INTERNAL
+GType                   g_simple_action_observer_get_type               (void);
+G_GNUC_INTERNAL
+GSimpleActionObserver * g_simple_action_observer_new                    (GActionObservable     *observable,
+                                                                         const gchar           *action_name,
+                                                                         GVariant              *target);
+G_GNUC_INTERNAL
+void                    g_simple_action_observer_activate               (GSimpleActionObserver *observer);
+G_GNUC_INTERNAL
+gboolean                g_simple_action_observer_get_active             (GSimpleActionObserver *observer);
+G_GNUC_INTERNAL
+gboolean                g_simple_action_observer_get_enabled            (GSimpleActionObserver *observer);
+
+G_END_DECLS
+
+#endif /* __G_SIMPLE_ACTION_OBSERVER_H__ */
index 0668636fc055a53216ff6d785dcc85083288aefd..88823e23810ae347b51573e47e9b4f0992296439 100644 (file)
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -36,6 +36,7 @@
 #include <gtk/gtkaccelmap.h>
 #include <gtk/gtkaccessible.h>
 #include <gtk/gtkaction.h>
+#include <gtk/gtkactionable.h>
 #include <gtk/gtkactiongroup.h>
 #include <gtk/gtkactivatable.h>
 #include <gtk/gtkadjustment.h>
diff --git a/gtk/gtkactionable.c b/gtk/gtkactionable.c
new file mode 100644 (file)
index 0000000..f78474e
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright © 2012 Canonical Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * Authors: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include "config.h"
+
+#include "gtkactionable.h"
+
+#include "gtkwidget.h"
+#include "gtkintl.h"
+
+G_DEFINE_INTERFACE (GtkActionable, gtk_actionable, GTK_TYPE_WIDGET)
+
+static void
+gtk_actionable_default_init (GtkActionableInterface *iface)
+{
+  g_object_interface_install_property (iface,
+    g_param_spec_string ("action-name", P_("action name"),
+                         P_("The name of the associated action, like 'app.quit'"),
+                         NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_interface_install_property (iface,
+    g_param_spec_variant ("action-target", P_("action target value"),
+                          P_("The parameter for action invocations"),
+                          G_VARIANT_TYPE_ANY, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+const gchar *
+gtk_actionable_get_action_name (GtkActionable *actionable)
+{
+  g_return_val_if_fail (GTK_IS_ACTIONABLE (actionable), NULL);
+
+  return GTK_ACTIONABLE_GET_IFACE (actionable)
+    ->get_action_name (actionable);
+}
+
+void
+gtk_actionable_set_action_name (GtkActionable *actionable,
+                                const gchar   *action_name)
+{
+  g_return_if_fail (GTK_IS_ACTIONABLE (actionable));
+
+  GTK_ACTIONABLE_GET_IFACE (actionable)
+    ->set_action_name (actionable, action_name);
+}
+
+GVariant *
+gtk_actionable_get_action_target_value (GtkActionable *actionable)
+{
+  g_return_val_if_fail (GTK_IS_ACTIONABLE (actionable), NULL);
+
+  return GTK_ACTIONABLE_GET_IFACE (actionable)
+    ->get_action_target_value (actionable);
+}
+
+void
+gtk_actionable_set_action_target_value (GtkActionable *actionable,
+                                        GVariant      *target_value)
+{
+  g_return_if_fail (GTK_IS_ACTIONABLE (actionable));
+
+  GTK_ACTIONABLE_GET_IFACE (actionable)
+    ->set_action_target_value (actionable, target_value);
+}
+
+void
+gtk_actionable_set_action_target (GtkActionable *actionable,
+                                  const gchar   *format_string,
+                                  ...)
+{
+  va_list ap;
+
+  va_start (ap, format_string);
+  gtk_actionable_set_action_target_value (actionable, g_variant_new_va (format_string, NULL, &ap));
+  va_end (ap);
+}
+
+void
+gtk_actionable_set_detailed_action_name (GtkActionable *actionable,
+                                         const gchar   *detailed_action_name)
+{
+  gchar **parts;
+
+  g_return_if_fail (GTK_IS_ACTIONABLE (actionable));
+
+  if (detailed_action_name == NULL)
+    {
+      gtk_actionable_set_action_name (actionable, NULL);
+      gtk_actionable_set_action_target_value (actionable, NULL);
+      return;
+    }
+
+  parts = g_strsplit (detailed_action_name, "::", 2);
+  gtk_actionable_set_action_name (actionable, parts[0]);
+  if (parts[0] && parts[1])
+    gtk_actionable_set_action_target (actionable, "s", parts[1]);
+  else
+    gtk_actionable_set_action_target_value (actionable, NULL);
+  g_strfreev (parts);
+}
diff --git a/gtk/gtkactionable.h b/gtk/gtkactionable.h
new file mode 100644 (file)
index 0000000..bef9bde
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2012 Canonical Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * Authors: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#ifndef __GTK_ACTIONABLE_H__
+#define __GTK_ACTIONABLE_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_ACTIONABLE                                 (gtk_actionable_get_type ())
+#define GTK_ACTIONABLE(inst)                                (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             GTK_TYPE_ACTIONABLE, GtkActionable))
+#define GTK_IS_ACTIONABLE(inst)                             (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             GTK_TYPE_ACTIONABLE))
+#define GTK_ACTIONABLE_GET_IFACE(inst)                      (G_TYPE_INSTANCE_GET_INTERFACE ((inst),                  \
+                                                             GTK_TYPE_ACTIONABLE, GtkActionableInterface))
+
+typedef struct _GtkActionableInterface                      GtkActionableInterface;
+typedef struct _GtkActionable                               GtkActionable;
+
+struct _GtkActionableInterface
+{
+  GTypeInterface g_iface;
+
+  const gchar * (* get_action_name)             (GtkActionable *actionable);
+  void          (* set_action_name)             (GtkActionable *actionable,
+                                                 const gchar   *action_name);
+  GVariant *    (* get_action_target_value)     (GtkActionable *actionable);
+  void          (* set_action_target_value)     (GtkActionable *actionable,
+                                                 GVariant      *action_target_value);
+};
+
+GType                   gtk_actionable_get_type                         (void) G_GNUC_CONST;
+
+const gchar *           gtk_actionable_get_action_name                  (GtkActionable *actionable);
+void                    gtk_actionable_set_action_name                  (GtkActionable *actionable,
+                                                                         const gchar   *action_name);
+
+GVariant *              gtk_actionable_get_action_target_value          (GtkActionable *actionable);
+void                    gtk_actionable_set_action_target_value          (GtkActionable *actionable,
+                                                                         GVariant      *target_value);
+
+void                    gtk_actionable_set_action_target                (GtkActionable *actionable,
+                                                                         const gchar   *format_string,
+                                                                         ...);
+
+void                    gtk_actionable_set_detailed_action_name         (GtkActionable *actionable,
+                                                                         const gchar   *detailed_action_name);
+
+G_END_DECLS
+
+#endif /* __GTK_ACTIONABLE_H__ */
index 9693f78d86fbcab6ce2a8386c5d37cffabe9532b..4ff3b2b27eecf51f44ed66d790ba30e5e5e6d432 100644 (file)
@@ -23,6 +23,7 @@
 #ifndef __GTK_APPLICATION_PRIVATE_H__
 #define __GTK_APPLICATION_PRIVATE_H__
 
+#include "gsimpleactionobserver.h"
 #include "gtkapplicationwindow.h"
 
 G_GNUC_INTERNAL
@@ -33,4 +34,9 @@ gboolean                gtk_application_window_publish                  (GtkAppl
 G_GNUC_INTERNAL
 void                    gtk_application_window_unpublish                (GtkApplicationWindow *window);
 
+G_GNUC_INTERNAL
+GSimpleActionObserver * gtk_application_window_get_observer             (GtkApplicationWindow *window,
+                                                                         const gchar          *action_name,
+                                                                         GVariant             *target);
+
 #endif /* __GTK_APPLICATION_PRIVATE_H__ */
index 5f1d57c83d69bb61a832213701189d78ae834019..341e57709a530019e8225d93be3a3de8d878ffec 100644 (file)
@@ -182,6 +182,7 @@ struct _GtkApplicationWindowPrivate
 {
   GSimpleActionGroup *actions;
   GActionObservable *muxer;
+  gboolean muxer_initialised;
   GtkWidget *menubar;
   GtkAccelGroup *accels;
   GSList *accel_closures;
@@ -671,14 +672,11 @@ gtk_application_window_real_realize (GtkWidget *widget)
   g_signal_connect (settings, "notify::gtk-shell-shows-menubar",
                     G_CALLBACK (gtk_application_window_shell_shows_menubar_changed), window);
 
-  if (window->priv->muxer == NULL)
+  if (!window->priv->muxer_initialised)
     {
-      GActionMuxer *muxer;
-
-      muxer = g_action_muxer_new ();
-      g_action_muxer_insert (muxer, "app", G_ACTION_GROUP (application));
-      g_action_muxer_insert (muxer, "win", G_ACTION_GROUP (window));
-      window->priv->muxer = G_ACTION_OBSERVABLE (muxer);
+      g_action_muxer_insert (G_ACTION_MUXER (window->priv->muxer), "app", G_ACTION_GROUP (application));
+      g_action_muxer_insert (G_ACTION_MUXER (window->priv->muxer), "win", G_ACTION_GROUP (window));
+      window->priv->muxer_initialised = TRUE;
     }
 
   gtk_application_window_update_shell_shows_app_menu (window, settings);
@@ -875,6 +873,8 @@ gtk_application_window_init (GtkApplicationWindow *window)
                             G_CALLBACK (g_action_group_action_state_changed), window);
   g_signal_connect_swapped (window->priv->actions, "action-removed",
                             G_CALLBACK (g_action_group_action_removed), window);
+
+  window->priv->muxer = G_ACTION_OBSERVABLE (g_action_muxer_new ());
 }
 
 static void
@@ -982,3 +982,13 @@ gtk_application_window_set_show_menubar (GtkApplicationWindow *window,
       g_object_notify_by_pspec (G_OBJECT (window), gtk_application_window_properties[PROP_SHOW_MENUBAR]);
     }
 }
+
+GSimpleActionObserver *
+gtk_application_window_get_observer (GtkApplicationWindow *window,
+                                     const gchar          *action_name,
+                                     GVariant             *target)
+{
+  g_return_val_if_fail (GTK_IS_APPLICATION_WINDOW (window), NULL);
+
+  return g_simple_action_observer_new (window->priv->muxer, action_name, target);
+}
index 3b3b5b28d6a6f5e513462fb85ef87665df353627..f7a908443bd8274df3e8c1edc680db988bcd228c 100644 (file)
@@ -58,7 +58,8 @@
 #include "gtkprivate.h"
 #include "gtkintl.h"
 #include "a11y/gtkbuttonaccessible.h"
-
+#include "gtkapplicationprivate.h"
+#include "gtkactionable.h"
 
 static const GtkBorder default_default_border = { 1, 1, 1, 1 };
 static const GtkBorder default_default_outside_border = { 0, 0, 0, 0 };
@@ -90,6 +91,8 @@ enum {
   PROP_XALIGN,
   PROP_YALIGN,
   PROP_IMAGE_POSITION,
+  PROP_ACTION_NAME,
+  PROP_ACTION_TARGET,
 
   /* activatable properties */
   PROP_ACTIVATABLE_RELATED_ACTION,
@@ -147,8 +150,11 @@ static void gtk_button_state_changed   (GtkWidget             *widget,
                                        GtkStateType           previous_state);
 static void gtk_button_grab_notify     (GtkWidget             *widget,
                                        gboolean               was_grabbed);
+static void gtk_button_hierarchy_changed (GtkWidget           *widget,
+                                          GtkWidget           *previous_toplevel);
 
 
+static void gtk_button_actionable_iface_init     (GtkActionableInterface *iface);
 static void gtk_button_activatable_interface_init(GtkActivatableIface  *iface);
 static void gtk_button_update                    (GtkActivatable       *activatable,
                                                  GtkAction            *action,
@@ -170,6 +176,7 @@ static void gtk_button_get_preferred_height      (GtkWidget           *widget,
 static guint button_signals[LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_BIN,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, gtk_button_actionable_iface_init)
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
                                                gtk_button_activatable_interface_init))
 
@@ -208,6 +215,7 @@ gtk_button_class_init (GtkButtonClass *klass)
   widget_class->leave_notify_event = gtk_button_leave_notify;
   widget_class->state_changed = gtk_button_state_changed;
   widget_class->grab_notify = gtk_button_grab_notify;
+  widget_class->hierarchy_changed = gtk_button_hierarchy_changed;
 
   container_class->child_type = gtk_button_child_type;
   container_class->add = gtk_button_add;
@@ -330,6 +338,9 @@ gtk_button_class_init (GtkButtonClass *klass)
                                                       GTK_POS_LEFT,
                                                       GTK_PARAM_READWRITE));
 
+  g_object_class_override_property (gobject_class, PROP_ACTION_NAME, "action-name");
+  g_object_class_override_property (gobject_class, PROP_ACTION_TARGET, "action-target");
+
   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");
 
@@ -584,6 +595,12 @@ gtk_button_destroy (GtkWidget *widget)
       priv->label_text = NULL;
     }
 
+  if (priv->action_name)
+    {
+      g_free (priv->action_name);
+      priv->action_name = NULL;
+    }
+
   GTK_WIDGET_CLASS (gtk_button_parent_class)->destroy (widget);
 }
 
@@ -666,6 +683,8 @@ gtk_button_dispose (GObject *object)
   GtkButton *button = GTK_BUTTON (object);
   GtkButtonPrivate *priv = button->priv;
 
+  g_clear_object (&priv->action_observer);
+
   if (priv->action)
     {
       gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), NULL);
@@ -674,6 +693,83 @@ gtk_button_dispose (GObject *object)
   G_OBJECT_CLASS (gtk_button_parent_class)->dispose (object);
 }
 
+static void
+gtk_button_update_action_observer (GtkButton *button)
+{
+  GtkWidget *window;
+
+  g_signal_handlers_disconnect_by_func (button, gtk_real_button_clicked, NULL);
+
+  /* we are the only owner so this will clear all the signals */
+  g_clear_object (&button->priv->action_observer);
+
+  window = gtk_widget_get_toplevel (GTK_WIDGET (button));
+
+  if (GTK_IS_APPLICATION_WINDOW (window) && button->priv->action_name)
+    {
+      GSimpleActionObserver *observer;
+
+      observer = gtk_application_window_get_observer (GTK_APPLICATION_WINDOW (window),
+                                                      button->priv->action_name,
+                                                      button->priv->action_target);
+
+      _gtk_button_set_depressed (button, g_simple_action_observer_get_active (observer));
+
+      if (g_object_class_find_property (G_OBJECT_GET_CLASS (button), "active"))
+        g_object_bind_property (observer, "active", button, "active", G_BINDING_SYNC_CREATE);
+      g_object_bind_property (observer, "enabled", button, "sensitive", G_BINDING_SYNC_CREATE);
+
+      button->priv->action_observer = observer;
+
+      g_signal_connect_after (button, "clicked", G_CALLBACK (gtk_real_button_clicked), NULL);
+    }
+}
+
+static void
+gtk_button_set_action_name (GtkActionable *actionable,
+                            const gchar   *action_name)
+{
+  GtkButton *button = GTK_BUTTON (actionable);
+
+  g_return_if_fail (GTK_IS_BUTTON (button));
+
+  if (g_strcmp0 (action_name, button->priv->action_name) != 0)
+    {
+      g_free (button->priv->action_name);
+      button->priv->action_name = g_strdup (action_name);
+
+      gtk_button_update_action_observer (button);
+
+      g_object_notify (G_OBJECT (button), "action-name");
+    }
+}
+
+static void
+gtk_button_set_action_target_value (GtkActionable *actionable,
+                                    GVariant      *action_target)
+{
+  GtkButton *button = GTK_BUTTON (actionable);
+
+  g_return_if_fail (GTK_IS_BUTTON (button));
+
+  if (action_target != button->priv->action_target &&
+      (!action_target || !button->priv->action_target ||
+       !g_variant_equal (action_target, button->priv->action_target)))
+    {
+      if (button->priv->action_target)
+        g_variant_unref (button->priv->action_target);
+
+      button->priv->action_target = NULL;
+
+      if (action_target)
+        button->priv->action_target = g_variant_ref_sink (action_target);
+
+      gtk_button_update_action_observer (button);
+
+      g_object_notify (G_OBJECT (button), "action-target");
+    }
+}
+
 static void
 gtk_button_set_property (GObject         *object,
                          guint            prop_id,
@@ -718,6 +814,12 @@ gtk_button_set_property (GObject         *object,
     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
       gtk_button_set_use_action_appearance (button, g_value_get_boolean (value));
       break;
+    case PROP_ACTION_NAME:
+      gtk_button_set_action_name (GTK_ACTIONABLE (button), g_value_get_string (value));
+      break;
+    case PROP_ACTION_TARGET:
+      gtk_button_set_action_target_value (GTK_ACTIONABLE (button), g_value_get_variant (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -768,12 +870,43 @@ gtk_button_get_property (GObject         *object,
     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
       g_value_set_boolean (value, priv->use_action_appearance);
       break;
+    case PROP_ACTION_NAME:
+      g_value_set_string (value, priv->action_name);
+      break;
+    case PROP_ACTION_TARGET:
+      g_value_set_variant (value, priv->action_target);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
     }
 }
 
+static const gchar *
+gtk_button_get_action_name (GtkActionable *actionable)
+{
+  GtkButton *button = GTK_BUTTON (actionable);
+
+  return button->priv->action_name;
+}
+
+static GVariant *
+gtk_button_get_action_target_value (GtkActionable *actionable)
+{
+  GtkButton *button = GTK_BUTTON (actionable);
+
+  return button->priv->action_target;
+}
+
+static void
+gtk_button_actionable_iface_init (GtkActionableInterface *iface)
+{
+  iface->get_action_name = gtk_button_get_action_name;
+  iface->set_action_name = gtk_button_set_action_name;
+  iface->get_action_target_value = gtk_button_get_action_target_value;
+  iface->set_action_target_value = gtk_button_set_action_target_value;
+}
+
 static void 
 gtk_button_activatable_interface_init (GtkActivatableIface  *iface)
 {
@@ -1822,6 +1955,9 @@ gtk_real_button_clicked (GtkButton *button)
 {
   GtkButtonPrivate *priv = button->priv;
 
+  if (priv->action_observer)
+    g_simple_action_observer_activate (priv->action_observer);
+
   if (priv->action)
     gtk_action_activate (priv->action);
 }
@@ -2416,6 +2552,28 @@ gtk_button_grab_notify (GtkWidget *widget,
     }
 }
 
+static void
+gtk_button_hierarchy_changed (GtkWidget *widget,
+                              GtkWidget *previous_toplevel)
+{
+  GtkButton *button = GTK_BUTTON (widget);
+  GtkWidgetClass *parent_class;
+
+  parent_class = GTK_WIDGET_CLASS (gtk_button_parent_class);
+  if (parent_class->hierarchy_changed)
+    parent_class->hierarchy_changed (widget, previous_toplevel);
+
+  if (button->priv->action_name)
+    {
+      GtkWidget *toplevel;
+
+      toplevel = gtk_widget_get_toplevel (widget);
+
+      if (toplevel != previous_toplevel)
+        gtk_button_update_action_observer (button);
+    }
+}
+
 /**
  * gtk_button_set_image:
  * @button: a #GtkButton
@@ -2525,7 +2683,6 @@ gtk_button_get_image_position (GtkButton *button)
   return button->priv->image_position;
 }
 
-
 /**
  * gtk_button_get_event_window:
  * @button: a #GtkButton
index 5db1bc1171f77ae370ae6f8e001680cac2bd892e..232973c1fcfe4822c3d87ad99878521ac3c0ec8d 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef __GTK_BUTTON_PRIVATE_H__
 #define __GTK_BUTTON_PRIVATE_H__
 
+#include "gsimpleactionobserver.h"
 #include "gtkaction.h"
 
 G_BEGIN_DECLS
@@ -29,6 +30,10 @@ struct _GtkButtonPrivate
   GtkAction             *action;
   GtkWidget             *image;
 
+  gchar                 *action_name;
+  GVariant              *action_target;
+  GSimpleActionObserver *action_observer;
+
   GdkDevice             *grab_keyboard;
   GdkWindow             *event_window;
 
index 934ca6132124209071d0b7d74459dabd44ad0ea7..ea67c34c6a05e6da1adc9b8f0e66cc75dd38983f 100644 (file)
@@ -32,6 +32,7 @@
 #include "gtkintl.h"
 #include "gtktoolbar.h"
 #include "gtkactivatable.h"
+#include "gtkactionable.h"
 #include "gtkprivate.h"
 
 #include <string.h>
@@ -81,7 +82,9 @@ enum {
   PROP_LABEL_WIDGET,
   PROP_STOCK_ID,
   PROP_ICON_NAME,
-  PROP_ICON_WIDGET
+  PROP_ICON_WIDGET,
+  PROP_ACTION_NAME,
+  PROP_ACTION_TARGET
 };
 
 static void gtk_tool_button_init          (GtkToolButton      *button,
@@ -107,6 +110,7 @@ static void gtk_tool_button_style_updated  (GtkWidget          *widget);
 
 static void gtk_tool_button_construct_contents (GtkToolItem *tool_item);
 
+static void gtk_tool_button_actionable_iface_init      (GtkActionableInterface *iface);
 static void gtk_tool_button_activatable_interface_init (GtkActivatableIface  *iface);
 static void gtk_tool_button_update                     (GtkActivatable       *activatable,
                                                        GtkAction            *action,
@@ -135,7 +139,6 @@ static GObjectClass        *parent_class = NULL;
 static GtkActivatableIface *parent_activatable_iface;
 static guint                toolbutton_signals[LAST_SIGNAL] = { 0 };
 
-
 GType
 gtk_tool_button_get_type (void)
 {
@@ -143,6 +146,12 @@ gtk_tool_button_get_type (void)
   
   if (!type)
     {
+      const GInterfaceInfo actionable_info =
+      {
+        (GInterfaceInitFunc) gtk_tool_button_actionable_iface_init,
+        (GInterfaceFinalizeFunc) NULL,
+        NULL
+      };
       const GInterfaceInfo activatable_info =
       {
         (GInterfaceInitFunc) gtk_tool_button_activatable_interface_init,
@@ -158,6 +167,7 @@ gtk_tool_button_get_type (void)
                                            (GInstanceInitFunc) gtk_tool_button_init,
                                            0);
 
+      g_type_add_interface_static (type, GTK_TYPE_ACTIONABLE, &actionable_info);
       g_type_add_interface_static (type, GTK_TYPE_ACTIVATABLE,
                                    &activatable_info);
     }
@@ -278,6 +288,9 @@ gtk_tool_button_class_init (GtkToolButtonClass *klass)
                                                        GTK_TYPE_WIDGET,
                                                        GTK_PARAM_READWRITE));
 
+  g_object_class_override_property (object_class, PROP_ACTION_NAME, "action-name");
+  g_object_class_override_property (object_class, PROP_ACTION_TARGET, "action-target");
+
   /**
    * GtkButton:icon-spacing:
    * 
@@ -612,6 +625,12 @@ gtk_tool_button_set_property (GObject         *object,
     case PROP_ICON_WIDGET:
       gtk_tool_button_set_icon_widget (button, g_value_get_object (value));
       break;
+    case PROP_ACTION_NAME:
+      g_object_set_property (G_OBJECT (button->priv->button), "action-name", value);
+      break;
+    case PROP_ACTION_TARGET:
+      g_object_set_property (G_OBJECT (button->priv->button), "action-target", value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -660,12 +679,61 @@ gtk_tool_button_get_property (GObject         *object,
     case PROP_ICON_WIDGET:
       g_value_set_object (value, button->priv->icon_widget);
       break;
+    case PROP_ACTION_NAME:
+      g_object_get_property (G_OBJECT (button->priv->button), "action-name", value);
+      break;
+    case PROP_ACTION_TARGET:
+      g_object_get_property (G_OBJECT (button->priv->button), "action-target", value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
     }
 }
 
+static const gchar *
+gtk_tool_button_get_action_name (GtkActionable *actionable)
+{
+  GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
+
+  return gtk_actionable_get_action_name (GTK_ACTIONABLE (button->priv->button));
+}
+
+static void
+gtk_tool_button_set_action_name (GtkActionable *actionable,
+                                 const gchar   *action_name)
+{
+  GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
+
+  gtk_actionable_set_action_name (GTK_ACTIONABLE (button->priv->button), action_name);
+}
+
+static GVariant *
+gtk_tool_button_get_action_target_value (GtkActionable *actionable)
+{
+  GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
+
+  return gtk_actionable_get_action_target_value (GTK_ACTIONABLE (button->priv->button));
+}
+
+static void
+gtk_tool_button_set_action_target_value (GtkActionable *actionable,
+                                         GVariant      *action_target)
+{
+  GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
+
+  gtk_actionable_set_action_target_value (GTK_ACTIONABLE (button->priv->button), action_target);
+}
+
+static void
+gtk_tool_button_actionable_iface_init (GtkActionableInterface *iface)
+{
+  iface->get_action_name = gtk_tool_button_get_action_name;
+  iface->set_action_name = gtk_tool_button_set_action_name;
+  iface->get_action_target_value = gtk_tool_button_get_action_target_value;
+  iface->set_action_target_value = gtk_tool_button_set_action_target_value;
+}
+
 static void
 gtk_tool_button_finalize (GObject *object)
 {